#include "bbt/access_protocol/packet_buffer.h"

#include <stdlib.h>
#include <arpa/inet.h>
#include <iostream>

#include <gtest/gtest.h>

using namespace std;
using namespace bbt;

TEST(T, hton64) {
  int64_t v = hton64(0x467f);
  uint8_t * p = (uint8_t *)&v; 
  for (int i = 0; i < 8; ++i) {
    printf("%02x", p[i]);
    if (i % 8 == 7) {
      printf("\r\n");
    }
  }
  EXPECT_EQ(v, 0x7f46000000000000);
  EXPECT_EQ(ntoh64(v), 0x467f);
}

TEST(T, PacketReadBuffer) {
  char data[128];
  BbtPacketHeader * header = (BbtPacketHeader *)data;
  size_t body_length = 0x08;
  header->body_length = htons(body_length);
  header->protocol_type = htons(1);
  header->check_sum = htonl(0x7ff7);

  cout << "header->protocol_type = " << header->protocol_type << endl; 
  cout << "header->body_length = " << ntohs(header->body_length) << endl; 
  for(int i = 0;  i < ntohs(header->body_length); ++i) {
    *(header->body() + i) = 'a' + i;
  }

  PacketReadBuffer pb(false);
  const size_t kBufLen = 16 * 1024;

  memcpy(pb.FreeBuffer(), data, sizeof(BbtPacketHeader) + body_length);
  pb.OnDataPushed(sizeof(BbtPacketHeader) + body_length);
  EXPECT_EQ(pb.FreeSize(), kBufLen - (sizeof(BbtPacketHeader) + body_length));

  memcpy(pb.FreeBuffer(), data, sizeof(BbtPacketHeader) + body_length);
  pb.OnDataPushed(sizeof(BbtPacketHeader) + body_length);
  EXPECT_EQ(pb.FreeSize(), kBufLen - 2 * (sizeof(BbtPacketHeader) + body_length));

  memcpy(pb.FreeBuffer(), data, sizeof(BbtPacketHeader) + body_length);
  pb.OnDataPushed(sizeof(BbtPacketHeader) + body_length);
  EXPECT_EQ(pb.FreeSize(), kBufLen - 3 * (sizeof(BbtPacketHeader) + body_length));

  int i = 0;
  BbtPacketHeader * packet = NULL;
  while((packet = pb.PopFullPacketHeader())) {
    ++i;
    EXPECT_EQ(packet->check_sum, 0x7ff7);
    pb.Skip(packet->body_length);
  }
  EXPECT_TRUE(i == 3);
  EXPECT_TRUE(pb.IsValid());
  // EXPECT_EQ(pb.BufferSize(), kBufLen);
}

#pragma pack(1)

struct TestStruct {
};

#pragma pack()

TEST(T, PacketWriteBuffer) {
  // write buf test
  std::string content = "12345678901234567890";

  BbtPacketHeader write_header;
  write_header.protocol_type = 1;
  write_header.body_length = 54;
  write_header.check_sum = 0;
  
  // uint32_t padding_length = 8 - write_header.body_length % 8;
  // EXPECT_EQ(padding_length, 3);

  PacketWriteBuffer write_buf;
  int round = 10;
  for (int i = 0; i < round; ++i) {
    int sz = write_buf.PushPacketHeader(write_header);
    EXPECT_EQ(sz, 8);
    sz += write_buf.PushString(content.c_str());
    EXPECT_EQ(sz, 29);

    uint64_t i64 = 0x7864;
    sz += write_buf.PushInt64(i64);
    EXPECT_EQ(sz, 37);

    uint32_t i32 = 0x9c1064;
    sz += write_buf.PushUint32(i32);
    EXPECT_EQ(sz, 41);

    uint16_t i16 = 0x9c71;
    sz += write_buf.PushUint16(i16);
    EXPECT_EQ(sz, 43);

    uint8_t i8 = 0xca;
    sz += write_buf.PushUint8(i8);
    EXPECT_EQ(sz, 44);

    char bytes[7] = {0x0};
    sz += write_buf.PushBytes(bytes, sizeof(bytes));
    EXPECT_EQ(sz, 55);

    sz += write_buf.PushRawBytes(bytes, sizeof(bytes));
    EXPECT_EQ(sz, 62);

    sz += write_buf.PushPaddingBytes(2);
    EXPECT_EQ(sz, 64);

    EXPECT_EQ(write_buf.DataSize(), (64 * (i + 1)));
  }
  write_buf.PushPacketHeader(write_header);

  EXPECT_EQ(write_buf.DataSize(), round * 64 + sizeof(write_header));
  
  PacketReadBuffer read_buf;
  for (;;) {
    char * p = write_buf.DataBuffer();
    size_t sz = write_buf.DataSize();
    if (sz > 10) {
      sz = 10;
    }
    if (sz == 0) {
      break;
    }
    memcpy(read_buf.FreeBuffer(), p, sz);
    write_buf.OnDataPopped(sz);
    read_buf.OnDataPushed(sz);
  }

  EXPECT_EQ(read_buf.DataSize(), round * 64 + sizeof(write_header));

  BbtPacketHeader * pkt;
  int read_round = 0;
  while(pkt = read_buf.PopFullPacketHeader()) {
    ++read_round;
    char * s = NULL;
    int sz = read_buf.PopString(&s);
    EXPECT_EQ(strlen(s), 20);
    EXPECT_EQ(sz, 21);

    int64_t i64;
    sz += read_buf.PopInt64(&i64);
    EXPECT_EQ(sz, 29);
    EXPECT_EQ(i64, 0x7864);

    uint32_t i32;
    sz += read_buf.PopUint32(&i32);
    EXPECT_EQ(sz, 33);
    EXPECT_EQ(i32, 0x9c1064);

    uint16_t i16;
    sz += read_buf.PopUint16(&i16);
    EXPECT_EQ(sz, 35);
    EXPECT_EQ(i16, 0x9c71);

    uint8_t i8;
    sz += read_buf.PopUint8(&i8);
    EXPECT_EQ(sz, 36);
    EXPECT_EQ(i8, 0xca);

    char * p = NULL;
    uint32_t bytes;
    sz += read_buf.PopBytes(&p, &bytes);
    EXPECT_EQ(sz, 47);
    for(int32_t i = 0; i < bytes; ++i) {
      EXPECT_EQ(p[i], 0x0);
    }
    EXPECT_EQ(bytes, 7);

    sz += read_buf.Skip(7);
    EXPECT_EQ(sz, 54);

    sz += read_buf.Skip(2);
    EXPECT_EQ(sz, 56);
  }
  EXPECT_EQ(read_round, 10);
  EXPECT_EQ(read_buf.DataSize(), sizeof(write_header));
}

int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

